require(GGally, quietly = TRUE)
require(reshape2, quietly = TRUE)
require(tidyverse, quietly = TRUE, warn.conflicts = FALSE)
## Loading tidyverse: ggplot2
## Loading tidyverse: tibble
## Loading tidyverse: tidyr
## Loading tidyverse: readr
## Loading tidyverse: purrr
## Loading tidyverse: dplyr
## Conflicts with tidy packages ----------------------------------------------
## filter(): dplyr, stats
## lag(): dplyr, stats
library(ggfortify)
library(cluster)
library(ggdendro)
library(broom)
library(plotly)
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(readr)
library(magrittr)
##
## Attaching package: 'magrittr'
## The following object is masked from 'package:purrr':
##
## set_names
## The following object is masked from 'package:tidyr':
##
## extract
theme_set(theme_bw())
Neste post vamos investigar a existência de tipos de filmes quanto ao gênero do personagem e da quantidade de palavras que ele fala. Esta investigação vai ajudar as pessoas a se confrontarem com o que se conhece popularmente a respeito de filmes voltados para o público feminino e os filmes do gênero de terror, por exemplo. Será que os filmes femininos o número de personagens é predominantemente feito de mulheres? Será que elas falam em maior quantidade que os homens? Será que existem grupos que definem comportamentos comuns para os filmes analisados? Utilizaremos os dados cedidos pelo Github.
personagens = read_csv(file = "../dados/film-dialogue/character_list5.csv")
## Parsed with column specification:
## cols(
## script_id = col_integer(),
## imdb_character_name = col_character(),
## words = col_integer(),
## gender = col_character(),
## age = col_character()
## )
personagens = personagens %>%
filter(age != 'NULL') %>%
mutate(age = as.numeric(age))
filmes = read.csv(file = "../dados/film-dialogue/meta_data7.csv")
filmes = filmes %>%
filter(gross != 'NA', gross > 0)
filmes_personagens = merge(filmes, personagens, by="script_id")
mulheres = filmes_personagens %>%
filter(gender == 'f') %>%
group_by(script_id, imdb_id, title, year, gross) %>%
summarise(n_f=n(), words_f=median(words)) %>%
filter(n_f > 1)
homens = filmes_personagens %>%
filter(gender == 'm') %>%
group_by(script_id, imdb_id, title, year, gross) %>%
summarise(n_m=n(), words_m=median(words)) %>%
filter(n_m > 1)
dados = merge(mulheres, homens,
by=c('script_id','imdb_id','title','year','gross'))
duplicados = dados %>%
group_by(title) %>% filter(row_number() > 1)
dados = dados %>%
filter(!(title %in% duplicados$title))
dados = dados %>%
subset(select = -c(script_id,imdb_id,year,gross))
Observando os dados cedidos pelo repositório pude notar que o valor da variável idade, da tabela de personagens, não estava disponível ou continha valor nulo. Desta forma foi feita a filtragem dessas observações. A variável renda da tabela dos filmes tinha comportamento semelhente. Algumas observações continha valor não disponível ou então igual a zero, desta forma, eu achei que seria prudente filtra-los uma vez que, filmes sem valor de renda ou com valor de renda igual a zero não seriam relavantes na análise.
Uma limitação encontrada durante a análise foi o fato de alguns filmes possuirem o mesmo nome embora fossem diferentes então para submeter os dados para a análise eu tive que fazer a filtragem desses filmes com nomes repetidos também.
Esta análise só levará em consideração os filmes que contenham mais de um personagem de cada gênero.
As dimensões submetidas a análise foram 4 variáveis numéricas calculadas a partir do conjunto de dados cedido pelo Github mencionado acima. São elas: nº de personagens do sexo feminino no filme (n_f), mediana de palavras dos personagens do sexo feminino no filme (words_f), nº de personagens do sexo masculino no filme (n_m) e mediana de palavras dos personagens do sexo masculino no filme (words_m).
O conjunto de dados submetido a análise contém, para cada filme, uma observação com valores para cada variável mencionada acima. A escolha das variáveis acima visava obter a resposta para a seguinte pergunta: visando o gênero do personagem e a quantidade de palavras ditas por ele em um filme, quais os tipos de filmes? Filmes em que as mulheres são protagonistas? Filmes em que os homens são protagonistas?
dw = dados
# Escala de log
dw2 <- dw %>%
mutate_each(funs(log), 2:5)
dw2.scaled = dw2 %>%
mutate_each(funs(as.vector(scale(.))), 2:5)
dists = dw2.scaled %>%
column_to_rownames("title") %>%
dist(method = "euclidean")
hc = hclust(dists, method = "ward.D")
n_clusters = 4
dw2 <- dw2 %>%
mutate(cluster = hc %>%
cutree(k = n_clusters) %>%
as.character())
dw2.scaled <- dw2.scaled %>%
mutate(cluster = hc %>%
cutree(k = n_clusters) %>%
as.character())
dw2.long = melt(dw2.scaled, id.vars = c("title", "cluster"))
dw2.scaled = dw2.scaled %>%
select(-cluster) # Remove o cluster adicionado antes l? em cima via hclust
set.seed(123)
explorando_k = tibble(k = 1:15) %>%
group_by(k) %>%
do(
kmeans(select(dw2.scaled, -title),
centers = .$k,
nstart = 20) %>% glance()
)
## Warning: did not converge in 10 iterations
Antes de realizar o agrupamento precisamos escolher um bom valor para k (basicamente indica o número de grupos ou tipos que iremos identificar no conjunto de dados). Uma medida comumente usada no k-means é comparar a distância (quadrática) entre o centro dos clusters e o centro dos dados com a distância (quadrática) entre todos os pontos nos dados e o centro dos dados.
Aqui o centro dos dados é um ponto imaginário na média de todas as variáveis. Calculamos a distância do centro de cada cluster para o centro dos dados e multiplicamos pelo número de pontos nesse cluster. Somando esse valor para todos os clusters, temos betweenss abaixo. Se esse valor for próximo do somatório total das distâncias dos pontos para o centro dos dados (totss), os pontos estão próximos do centro de seu cluster. Essa proporção pode ser usada para definir um bom valor de k. Quando ela para de crescer, para de valer à pena aumentar k.
explorando_k %>%
ggplot(aes(x = k, y = betweenss / totss)) +
geom_line() +
geom_point()
Observando o gráfico acima fica fácil perceber que o melhor valor de k seria 4, já que a partir de k=4, betweenss começa a parar de crescer. O ponto k=4 é também conhecido como joelho da curva.
# O agrupamento de fato:
km = dw2.scaled %>%
select(-title) %>%
kmeans(centers = n_clusters, nstart = 20)
# O df em formato longo, para visualiza??o
dw2.scaled.km.long = km %>%
augment(dw2.scaled) %>% # Adiciona o resultado de km
# aos dados originais dw2.scaled em
# uma vari?vel chamada .cluster
gather(key = "variável",
value = "valor",
-title, -.cluster) # = move para long todas as
# vari?vies menos title
# e .cluster
dw2.scaled.km.long %>%
ggplot(aes(x = `variável`, y = valor, group = title, colour = .cluster)) +
geom_line(alpha = .5) +
facet_wrap(~ .cluster) +
xlab("Variável") +
ylab("Valor") +
ggtitle("Gráfico de coordenadas paralelas") +
theme(plot.title = element_text(hjust = 0.5))
Observando o gráfico acima e olhando a direção em que as linhas dos filmes cruzam e tocam cada uma das variáveis ou coordenadas podemos observar grupos que caracterizam os filmes que ali cabem.
Logo abaixo podemos ver a visualização 2D da redução de dimensionalidade das 4 dimensões mencionadas anteriormente.
autoplot(km, data = dw2.scaled, label = TRUE) %>%
ggplotly()
PC1 e PC2 são as duas variáveis criadas para substituir as 4 variáveis originais de antes da visualização. PC1 e PC2 são úteis se conseguirmos entender a relação delas com as variáveis originais. Na técnica denominada PCA, cada uma dessas novas variáveis é calculada a partir das 4 iniciais.
autoplot(km, data = dw2.scaled, size = 2,
colour = "grey",
loadings = TRUE, loadings.colour = 'blue',
loadings.label = TRUE,
loadings.label.size = 3) %>% ggplotly()
Observando o gráfico, words_m e words_f variam quando um ponto está mais à esquerda ou direita no gráfico (direção de PC1), mas não variam muito em função da posição de um ponto no eixo vertical (direção de PC2). Já n_f e n_m estão mais relacionada com PC2, enquanto words_m e words_f praticamente não estão.
Seguindo a mesma leitura, n_f e n_m variam principalmente na medida que os pontos estão mais acima ou abaixo no gráfico (PC2), mas também em função de quão à esquerda ou direita eles estão.
Outra forma de ver a informação que o gráfico mostra é vendo PC1 e PC2 como duas funções das 4 variáveis originais, vejamos abaixo.
pr.out <- prcomp(select(dw2.scaled, -title), scale=TRUE)
tidy(pr.out,"variables") %>%
filter(PC <= 2) %>%
spread(column, value)
## PC n_f n_m words_f words_m
## 1 1 0.3404238 0.5120426 -0.5113350 -0.6003836
## 2 2 0.6895981 -0.5043678 -0.4143086 0.3137132
Os valores na tabela são os coeficientes, e a leitura é que:
PC1 = 0.34n_f + 0.51n_m - 0.51words_f − 0.60words_m e PC2 = 0.69n_f - 0.50n_m - 0.41words_f + 0.31words_m.
Em PC1, mudar uma unidade nas 2 primeiras variáveis aumenta PC1 e faz com que um ponto esteja mais à direita no gráfico. Já words_f e words_m têm efeito negativo e de maior efeito por unidade do que as duas primeiras. A unidade aqui é em z-scores: todas as variáveis foram normalizadas com scale antes da redução de dimensionalidade, para que seu efeito ficasse comparável.
Em PC2, mudar uma unidade nas 2 primeiras variáveis aumenta PC2 e faz com que um ponto esteja mais à cima ou abaixo no gráfico.
Podemos perceber que as variáveis são pouco correlacionadas. De certa maneira, isso não significa que elas não tenham informação parecida. As variáveis words_m e words_f são pouco correlacionadas mas as duas são componentes principais para trazer informação para PC1, por isso, PC1 é uma função dessas 2 variáveis. Como n_m e n_f não trazem muita informação para PC1, o método PCA cria PC2 que vai ser representado por estas duas variáveis que são menos correlacionadas com as demais. Da mesma forma, n_f e n_m são pouco correlacionadas mas a união delas trás muita informação para PC2.
Podemos entender que existem 4 grupos de filmes segundo as 4 variáveis que usamos.
ggplotly(autoplot(km, data = dw2.scaled, label = TRUE))
No grupo 1 (nuvem vermelha), estão os filmes que tem valores no geral parecidos - baixos - de n_f e words_f e - altos - de n_m, mas que variam muito nos valores de words_m e words_f. Em outras palavras os filmes que possuem muitos homens, poucas mulheres e que as mulheres falam pouco, em análise anterior esse grupo recebeu o nome de filmes femininos.
No grupo 2 (a nuvem verde), estão os filmes que tem valores no geral parecidos – e altos – de n_f e words_m, mas que variam muito nos valores de words_m e words_f que tem. Em outras palavras os filmes que possuem muitas mulheres mas que elas falam menos do que os homens, segundo a análise do checkpoint 2 esse grupo recebeu o nome de Filmes masculinos.
No grupo 3 (a nuvem azul), estão os filmes que tem valores no geral bem parecidos com os valores dos filmes do grupo 4. Possui valores - altos - de n_f mas que possuem valores variados de words_f e words_m. Em outras palavras são os filmes em tanto homens quanto mulheres falam variadamente mas a maior parte dos personagens são mulheres, em outra análise esse grupo recebeu o nome de filmes feministas.
No grupo 4 (a nuvem lilás), estão os filmes que tem valores bem parecidos no geral. Possui valores - altos - de n_m e - baixos - de n_f mas posuem valores bem parecidos de words_m e words_f. Em outras palavras são os filmes em que ambos os sexos falam praticamente a mesma quantidade de palavras embora a maior parte dos personagens sejam do sexo masculino, em outra análise esse grupo recebeu o nome de filmes machistas.
Copyright © 2017 Marcos Nascimento